<template>
	<view class="timeLineContainer" ref="timeLineContainer" :style="{
      backgroundColor: backgroundColor,
		}" @touchstart="onTouchstart" @touchmove="onTouchmove" @touchend="onTouchend" @mousedown="onMousedown"
		@mousemove="onMousemove" @mouseout="onMouseout" @mouseleave="onMouseleave" @mouseup="onMouseup">
		<canvas class="canvas" ref="canvas" canvas-id="canvasId" style="width: 100%;height: 50px;"></canvas>
		<view class="windowList" ref="windowList" v-if="showWindowList && windowList && windowList.length > 1"
			@scroll="onWindowListScroll">
			<WindowListItem v-for="(item, index) in windowListInner" ref="WindowListItem" :key="index" :index="index"
				:data="item" :totalMS="totalMS" :startTimestamp="startTimestamp" :width="width" :active="item.active"
				@click_window_timeSegments="triggerClickWindowTimeSegments" @click="toggleActive(index)">
			</WindowListItem>
		</view>
	</view>
</template>

<script>
	import dayjs from 'dayjs'
	import WindowListItem from './WindowListItem'
	import {
		ONE_HOUR_STAMP,
		ZOOM,
		ZOOM_HOUR_GRID,
		ZOOM_DATE_SHOW_RULE,
		MOBILE_ZOOM_HOUR_GRID,
		MOBILE_ZOOM_DATE_SHOW_RULE
	} from './constant'

	export default {
		name: 'TimeLine',
		components: {
			WindowListItem
		},
		props: {
			// 初始时间，中点所在的时间，默认为当天0点
			initTime: {
				type: [Number, String],
				default: ''
			},
			// 显示预览的时间范围，即中间刻度所允许的时间范围
			/*
			  {
			    start: '2020-12-19 18:30:00',// 允许显示的最小时间
			    end: '2021-01-20 10:0:00'// 允许显示的最大时间
			  }
			*/
			timeRange: {
				type: Object,
				default () {
					return {}
				}
			},
			// 初始的时间分辨率
			initZoomIndex: {
				type: Number,
				default: 5 // 24小时
			},
			// 是否显示中间的竖线
			showCenterLine: {
				type: Boolean,
				default: true
			},
			// 中间竖线的样式
			centerLineStyle: {
				type: Object,
				default () {
					return {
						width: 2,
						color: '#fff'
					}
				}
			},
			// 日期时间文字颜色
			textColor: {
				type: String,
				default: 'rgba(151,158,167,1)'
			},
			// 鼠标滑过显示的时间文字颜色
			hoverTextColor: {
				type: String,
				default: 'rgb(194, 202, 215)'
			},
			// 时间线段颜色
			lineColor: {
				type: String,
				default: 'rgba(151,158,167,1)'
			},
			// 时间线段高度占时间轴高度的比例
			lineHeightRatio: {
				type: Object,
				default () {
					return {
						date: 0.3, // 0点时的日期线段高度
						time: 0.2, // 显示时间的线段高度
						none: 0.1, // 不显示时间的线段高度
						hover: 0.3 // 鼠标滑过时显示的时间段高度
					}
				}
			},
			// 鼠标滑过时是否显示实时所在的时间
			showHoverTime: {
				type: Boolean,
				default: true
			},
			// 格式化鼠标滑过时间
			hoverTimeFormat: {
				type: Function,
			},
			// 要显示的时间颜色段
			/*
			  {
			    beginTime: new Date('2021-01-19 14:30:00').getTime(),// 起始时间戳
			    endTime: new Date('2021-01-20 18:00:00').getTime(),// 结束时间戳
			    color: '#FA3239',// 颜色
			    startRatio: 0.65,// 高度的起始比例，即top=时间轴高度*startRatio
			    endRatio: 0.9// 高度的结束比例，即bottom=时间轴高度*endRatio
			  }
			*/
			timeSegments: {
				type: Array,
				default: () => []
			},
			// 时间轴背景颜色
			backgroundColor: {
				type: String,
				default: '#262626'
			},
			// 是否允许切换分辨率
			enableZoom: {
				type: Boolean,
				default: true
			},
			// 是否允许拖动
			enableDrag: {
				type: Boolean,
				default: true
			},
			// 窗口列表，如果窗口数量大于1的话可以配置此项，会显示和窗口对应数量的时间轴，只有一个窗口的话请直接使用基本时间轴
			/*
			  {
			    timeSegments: [// 时间段
			      {
			        beginTime: new Date('2021-01-19 14:30:00').getTime(),// 起始时间戳
			        endTime: new Date('2021-01-20 18:00:00').getTime(),// 结束时间戳
			        color: '#FA3239',// 颜色
			        startRatio: 0.65,// 高度的起始比例，即top=时间轴高度*startRatio
			        endRatio: 0.9// 高度的结束比例，即bottom=时间轴高度*endRatio
			      }
			    ],
			    // 你的其他附加信息...
			  }
			*/
			windowList: {
				type: Array,
				default () {
					return []
				}
			},
			// 当显示windowList时的基础时间轴高度
			baseTimeLineHeight: {
				type: Number,
				default: 50
			},
			// 初始选中的窗口时间轴
			initSelectWindowTimeLineIndex: {
				type: Number,
				default: -1
			},
			// 是否是手机端
			isMobile: {
				type: Boolean,
				default:  uni.getSystemInfoSync().platform == 'android' ||  uni.getSystemInfoSync().platform == 'ios' ? true:false
			},
			// 鼠标按下和松开的距离小于该值认为是点击事件
			maxClickDistance: {
				type: Number,
				default: 3
			},
			// 绘制时间段时对计算出来的坐标进行四舍五入，可以防止相连的时间段绘制出来有间隔的问题
			roundWidthTimeSegments: {
				type: Boolean,
				default: true
			},
			// 自定义显示哪些时间
			customShowTime: {
				type: Function,
			},
			// 0点处是否显示日期
			showDateAtZero: {
				type: Boolean,
				default: true
			},
			// 扩展ZOOM列表，这个数组的数据会追加到内部的ZOOM数组，对应的zoomIndex往后累加即可，内部一共有11个zoom，那么你追加了一项，对应的zoomIndex为11，因为是从零开始计数
			// 数组类型，数组的每一项为：
			/*
			  {
			    zoom: 26,// 时间分辨率，整个时间轴表示的时间范围，单位：小时
			    zoomHourGrid: 0.5,// 时间分辨率对应的每格小时数，即时间轴上最小格代表多少小时
			    mobileZoomHourGrid: 2, // 手机模式下时间分辨率对应的每格小时数，如果不用适配手机端，可以不用设置
			  }
			*/
			// 同时你需要传递customShowTime属性来自定义控制时间显示，否则会报错，因为内置的规则只有11个
			extendZOOM: {
				type: Array,
				default () {
					return []
				}
			},
			// 格式化时间轴显示时间
			formatTime: {
				type: Function
			}
		},
		data() {
			return {
				width: 0,
				height: 0,
				ctx: null,
				currentZoomIndex: 0,
				currentTime: 0,
				startTimestamp: 0,
				mousedown: false,
				mousedownX: 0,
				mousedownY: 0,
				mousedownCacheStartTimestamp: 0,
				showWindowList: false,
				windowListInner: [],
				mousemoveX: -1,
				watchTimeList: [],

				clientOffset: []
			}
		},
		computed: {
			// 整个时间轴所代表的毫秒数
			totalMS() {
				return ZOOM[this.currentZoomIndex] * ONE_HOUR_STAMP
			},
			// 时间范围的时间戳表示
			timeRangeTimestamp() {
				let t = {}
				if (this.timeRange.start) {
					t.start = typeof this.timeRange.start === 'number' ? this.timeRange.start : new Date(this.timeRange
						.start).getTime()
				}
				if (this.timeRange.end) {
					t.end = typeof this.timeRange.end === 'number' ? this.timeRange.end : new Date(this.timeRange.end)
						.getTime()
				}
				return t
			},
			ACT_ZOOM_HOUR_GRID() {
				return this.isMobile ? MOBILE_ZOOM_HOUR_GRID : ZOOM_HOUR_GRID
				// return this.isMobile ? ZOOM_HOUR_GRID : ZOOM_HOUR_GRID
			},
			ACT_ZOOM_DATE_SHOW_RULE() {
				return this.isMobile ? MOBILE_ZOOM_DATE_SHOW_RULE : ZOOM_DATE_SHOW_RULE
				// return this.isMobile ? ZOOM_DATE_SHOW_RULE : ZOOM_DATE_SHOW_RULE
			},
			// 年月模式
			yearMonthMode() {
				return this.currentZoomIndex === 9;
			},
			// 年模式
			yearMode() {
				return this.currentZoomIndex === 10;
			},
		},
		watch: {
			timeSegments: {
				deep: true,
				handler: 'reRender'
			}
		},
		created() {
			this.extendZOOM.forEach((item) => {
				ZOOM.push(item.zoom)
				ZOOM_HOUR_GRID.push(item.zoomHourGrid)
				MOBILE_ZOOM_HOUR_GRID.push(item.mobileZoomHourGrid)
			})
		},
		mounted() {
			this.setInitData()
			this.init()
			this.draw()

			window.addEventListener('resize', this.onResize)
		},
		beforeDestroy() {
			window.removeEventListener('resize', this.onResize)
		},
		methods: {
			/**
			 * @Author: 王林25
			 * @Date: 2021-01-19 20:20:45
			 * @Desc: 设置初始数据
			 */
			setInitData() {
				// 内部窗口列表数据
				this.windowListInner = this.windowList.map((item, index) => {
					return {
						...item,
						active: this.initSelectWindowTimeLineIndex === index
					}
				})
				// 必须先设置currentZoomIndex
				// 初始时间分辨率
				this.currentZoomIndex =
					this.initZoomIndex >= 0 && this.initZoomIndex < ZOOM.length ?
					this.initZoomIndex :
					5
				// 初始当前时间
				this.startTimestamp =
					(this.initTime ?
						typeof this.initTime === 'number' ? this.initTime : new Date(this.initTime).getTime() :
						new Date(dayjs().format('YYYY-MM-DD 00:00:00')).getTime()) -
					this.totalMS / 2
				// 根据时间范围检查并修正起始时间
				this.fixStartTimestamp()
			},

			/**
			 * @Author: 王林25
			 * @Date: 2021-01-20 16:01:21
			 * @Desc: 根据时间范围检查并修正起始时间
			 */
			fixStartTimestamp() {
				let hfms = this.totalMS / 2
				let ct = this.startTimestamp + hfms
				if (this.timeRangeTimestamp.start && ct < this.timeRangeTimestamp.start) {
					this.startTimestamp = this.timeRangeTimestamp.start - hfms
				}
				if (this.timeRangeTimestamp.end && ct > this.timeRangeTimestamp.end) {
					this.startTimestamp = this.timeRangeTimestamp.end - hfms
				}
			},

			/**
			 * @Author: 王林25
			 * @Date: 2020-04-14 09:20:22
			 * @Desc: 初始化
			 */
			init() {
				console.log("init");
				this.ctx = uni.createCanvasContext('canvasId')



				let view = uni.createSelectorQuery().in(this).select(".timeLineContainer");
				let size = null
				view
					.boundingClientRect((data) => {
						// console.log("得到布局位置信息" + JSON.stringify(data));

						let {
							width,
							height
						} = data;

						this.width = width
						this.height =
							this.windowList.length > 1 ? this.baseTimeLineHeight : height


						this.ctx.setFillStyle("#000000");
						this.ctx.fillRect(0, 0, this.width, this.height);
						this.ctx.draw(true)
						this.showWindowList = true

					})
					.exec();

			},

			/**
			 * @Author: 王林25
			 * @Date: 2020-04-14 09:27:18
			 * @Desc: 绘制方法
			 */
			draw() {
				// 顺序很重要，不然层级不对
				this.drawTimeSegments()
				this.addGraduations()
				// this.drawMiddleLine()
				
				this.drawCurrentTimeLine()

				// this.currentTime = this.startTimestamp + this.totalMS / 2
				// this.$emit('timeChange', this.currentTime)

				// 通知窗口时间轴渲染
				try {
					this.$refs.WindowListItem.forEach((item) => {
						item.draw()
					})
				} catch (error) {}

				// 更新观察的时间位置
				this.updateWatchTime()
			},

			/**
			 * @Author: 王林25
			 * @Date: 2021-01-21 10:50:11
			 * @Desc:  更新观察的时间位置
			 */
			updateWatchTime() {
				this.watchTimeList.forEach((item) => {
					// 当前不在显示范围内
					if (item.time < this.startTimestamp || item.time > this.startTimestamp + this.totalMS) {
						item.callback(-1, -1)
					} else { // 在范围内
						let x = (item.time - this.startTimestamp) * (this.width / this.totalMS)
						let y = 0

						let view = uni.createSelectorQuery().in(this).select(".canvas");

						view
							.boundingClientRect(async (data) => {
								// console.log("得到布局位置信息" + JSON.stringify(data));
								let {
									left,
									top
								} = data;
								if (item.windowTimeLineIndex !== -1 && this.windowList.length > 1 && item
									.windowTimeLineIndex >= 0 && item.windowTimeLineIndex < this.windowList
									.length) {
									let rect = await this.$refs.WindowListItem[item.windowTimeLineIndex]
										.getRect()
									y = rect ? rect.top : top
								} else {
									y = top
								}
								item.callback(x + left, y)

							})
							.exec();


					}
				})
			},

			/**
			 * @Author: 王林25
			 * @Date: 2020-04-14 09:27:46
			 * @Desc: 绘制中间的竖线
			 */
			drawMiddleLine() {
				if (!this.showCenterLine) {
					return
				}
				this.ctx.beginPath()
				let {
					width,
					color
				} = this.centerLineStyle
				let x = this.width / 2
				this.drawLine(x, 0, x, this.height, width, color)
			},

			/**
			 * @Author: 王林25
			 * @Date: 2020-04-14 11:03:44
			 * @Desc: 绘制时间刻度
			 */
			addGraduations() {
				this.ctx.beginPath()
				// 一共可以绘制的格数
				let gridNum =
					ZOOM[this.currentZoomIndex] / this.ACT_ZOOM_HOUR_GRID[this.currentZoomIndex]
				// 一格多少毫秒
				let msPerGrid = this.ACT_ZOOM_HOUR_GRID[this.currentZoomIndex] * ONE_HOUR_STAMP
				// 每格间距，一格多少像素宽
				let pxPerGrid = this.width / gridNum
				// 起始偏移距离
				let msOffset = msPerGrid - (this.startTimestamp % msPerGrid)
				let pxOffset = (msOffset / msPerGrid) * pxPerGrid
				for (let i = 0; i < gridNum; i++) {
					let currentStartTimestamp = this.startTimestamp + msOffset + i * msPerGrid
					let adjustMsOffset = 0
					// 分辨率以年为单位
					if (this.yearMode) {
						adjustMsOffset = currentStartTimestamp - new Date(
							`${dayjs(currentStartTimestamp).format('YYYY')}-01-01 00:00:00`).getTime()
					} else if (this.yearMonthMode) {
						// 分辨率以月为单位
						adjustMsOffset = currentStartTimestamp - new Date(
							`${dayjs(currentStartTimestamp).format('YYYY')}-${dayjs(currentStartTimestamp).format('MM')}-01 00:00:00`
						).getTime()
					}
					let x = pxOffset + i * pxPerGrid - (adjustMsOffset / msPerGrid) * pxPerGrid
					let graduationTime = currentStartTimestamp - adjustMsOffset
					let h = 0
					let date = new Date(graduationTime)
					// 0点显示日期
					if (this.showDateAtZero && date.getHours() === 0 && date.getMinutes() === 0) {
						h = this.height * (this.lineHeightRatio.date === undefined ? 0.3 : this.lineHeightRatio.date)
						this.ctx.setFillStyle(this.textColor)
						this.ctx.fillText(
							this.graduationTitle(graduationTime),
							x - 13,
							h + 15
						)
					} else if (this.checkShowTime(date)) {
						// 其余时间根据各自规则显示
						h = this.height * (this.lineHeightRatio.time === undefined ? 0.2 : this.lineHeightRatio.time)
						this.ctx.setFillStyle(this.textColor)
						this.ctx.fillText(
							this.graduationTitle(graduationTime),
							x - 13,
							h + 15
						)
					} else {
						// 不显示时间的线段
						h = this.height * (this.lineHeightRatio.none === undefined ? 0.1 : this.lineHeightRatio.none)
					}
					this.drawLine(x, 0, x, h, 1, this.lineColor)
				}
			},
			
			
			    // 画当前时间线
			    drawCurrentTimeLine() {
			      this.ctx.beginPath()
			      let { width, color } = this.centerLineStyle;
			      // 画当前时间的线， 如果不在时间范围内则不画
			      const { nextDay } = this.getCurrentDayAndPreDay();
			      // 取最小的结束值，防止越界超出
			      const endTimeStamp = Math.min(this.startTimestamp + this.totalMS, nextDay);
			      if (this.currentTime < this.startTimestamp || this.currentTime > endTimeStamp) return;
			
			      const currentTimeMs = this.currentTime - this.startTimestamp;
			      // console.log(this.currentTime, this.startTimestamp)
			      // 一共可绘制的格数
			      let gridNum = ZOOM[this.currentZoomIndex] / this.ACT_ZOOM_HOUR_GRID[this.currentZoomIndex];
			      // 一格多少毫秒
			      let msPerGrid = this.ACT_ZOOM_HOUR_GRID[this.currentZoomIndex] * ONE_HOUR_STAMP;
			      // 每格的宽度
			      let pxPerGrid = this.width / gridNum;
			
			      // 当前时间线距离左边的时间刻度
			      const x = (currentTimeMs / msPerGrid) * pxPerGrid;
			      this.drawLine(x, 0, x, this.height, width, color)
			    },
			
			    // 获取当天和下一天的时间戳
			    getCurrentDayAndPreDay() {
			      const now = new Date();
			      const nextDay = new Date(now);
			      nextDay.setDate(now.getDate() + 1);
			      return {
			        now,
			        nextDay,
			      };
			    },

			// 判断是否需要显示该时间
			checkShowTime(date) {
				if (this.customShowTime) {
					let res = this.customShowTime(date, this.currentZoomIndex)
					if (res === true) {
						return true
					} else if (res === false) {
						return false
					}
				}
				return this.ACT_ZOOM_DATE_SHOW_RULE[this.currentZoomIndex](date)
			},

			/**
			 * @Author: 王林25
			 * @Date: 2020-04-14 15:42:49
			 * @Desc: 绘制时间段
			 */
			drawTimeSegments(callback, path) {
				const PX_PER_MS = this.width / this.totalMS // px/ms，每毫秒占的像素
				this.timeSegments.forEach((item) => {
					if (
						item.beginTime <= this.startTimestamp + this.totalMS
					) {
						let hasEndTime = item.endTime >= this.startTimestamp
						this.ctx.beginPath()
						let x = (item.beginTime - this.startTimestamp) * PX_PER_MS
						let w
						if (x < 0) {
							x = 0
							w = hasEndTime ? (item.endTime - this.startTimestamp) * PX_PER_MS : 1
						} else {
							w = hasEndTime ? (item.endTime - item.beginTime) * PX_PER_MS : 1
						}
						let heightStartRatio = item.startRatio === undefined ? 0.6 : item.startRatio
						let heightEndRatio = item.endRatio === undefined ? 0.9 : item.endRatio
						if (this.roundWidthTimeSegments) {
							x = Math.round(x)
							w = Math.round(w)
						}
						// 避免时间段小于1px绘制不出来
						w = Math.max(1, w)
						if (path) {
							this.ctx.rect(
								x,
								this.height * heightStartRatio,
								w,
								this.height * (heightEndRatio - heightStartRatio)
							)
						} else {
							this.ctx.setFillStyle(item.color)
							this.ctx.fillRect(
								x,
								this.height * heightStartRatio,
								w,
								this.height * (heightEndRatio - heightStartRatio)
							)
						}
						callback && callback(item)
					}
				})

			},

			// 触摸开始事件
			onTouchstart(e) {
				// console.log("触摸开始事件", e);
				if (!this.isMobile) {
					return
				}
				e = e.touches[0]
				this.onPointerdown(e)
			},

			/**
			 * @Author: 王林25
			 * @Date: 2020-04-14 14:29:40
			 * @Desc: 鼠标按下事件
			 */
			onMousedown(e) {
				if (this.isMobile) {
					return
				}
				this.onPointerdown(e)
			},

			// 按下事件
			async onPointerdown(e) {
				// let pos = this.getClientOffset(e)
				let pos = await this.getClientOffset(e)
				this.mousedownX = pos[0]
				this.mousedownY = pos[1]
				this.mousedown = true
				this.mousedownCacheStartTimestamp = this.startTimestamp
				this.$emit('mousedown', e)
			},

			// 触摸结束事件
			onTouchend(e) {
				// console.log("触摸结束事件", e);
				if (!this.isMobile) {
					return
				}
				/**
				 * targetTouches - 当前元素目标上 touch 列表。
				 * touches - 当前屏幕上的手指触摸 touch 列表。
				 * changedTouches - 触发当前事件的触摸 touch 列表。
				 * */
				e = e.touches[0] || e.changedTouches[0]
				this.onPointerup(e)
			},

			/**
			 * @Author: 王林25
			 * @Date: 2020-04-14 14:38:30
			 * @Desc: 鼠标松开
			 */
			onMouseup(e) {
				if (this.isMobile) {
					return
				}
				this.onPointerup(e)
			},

			// 松开事件
			async onPointerup(e) {
				// 触发click事件
				// let pos = this.getClientOffset(e)
				let pos = await this.getClientOffset(e)
				const reset = () => {
					this.mousedown = false
					this.mousedownX = 0
					this.mousedownY = 0
					this.mousedownCacheStartTimestamp = 0
				}
				if (
					Math.abs(pos[0] - this.mousedownX) <= this.maxClickDistance &&
					Math.abs(pos[1] - this.mousedownY) <= this.maxClickDistance
				) {
					reset()
					this.onClick(...pos)
					return
				}
				if (this.mousedown && this.enableDrag) {
					reset()
					this.$emit('dragTimeChange', this.currentTime)
				} else {
					reset()
				}
				this.$emit('mouseup', e)
			},

			// 触摸移动事件
			onTouchmove(e) {
				// console.log("触摸移动事件", e.touches[0]);
				if (!this.isMobile) {
					return
				}
				e = e.touches[0]
				this.onPointermove(e)
			},

			/**
			 * @Author: 王林25
			 * @Date: 2020-04-14 14:17:02
			 * @Desc: 鼠标移动事件
			 */
			onMousemove(e) {

				if (this.isMobile) {
					return
				}
				this.onPointermove(e)
			},

			// 移动事件
			async onPointermove(e) {
				let xx = await this.getClientOffset(e)
				// console.log(xx);
				// console.log(this.clientOffset);
				// let x = this.getClientOffset(e)[0]
				let x = xx[0]
				this.mousemoveX = x
				// 按下拖动
				if (this.mousedown && this.enableDrag) {
					this.drag(x)
				} else if (this.showHoverTime) {
					// 未按下显示鼠标所在时间
					this.hoverShow(x)
				}
			},

			/**
			 * @Author: 王林25
			 * @Date: 2021-01-21 10:40:37
			 * @Desc: 鼠标移出事件
			 */
			onMouseleave() {
				this.mousemoveX = -1
			},

			/**
			 * @Author: 王林25
			 * @Date: 2021-01-20 15:29:46
			 * @Desc: 按下拖动
			 */
			drag(x) {
				if (!this.enableDrag) {
					return
				}
				const PX_PER_MS = this.width / this.totalMS // px/ms
				let diffX = x - this.mousedownX
				// 判断是否超出限制范围
				let hfms = this.totalMS / 2
				let _newStartTimestamp =
					this.mousedownCacheStartTimestamp - Math.round(diffX / PX_PER_MS)
				let ct = _newStartTimestamp + hfms
				if (this.timeRangeTimestamp.start && ct < this.timeRangeTimestamp.start) {
					_newStartTimestamp = this.timeRangeTimestamp.start - hfms
				}
				if (this.timeRangeTimestamp.end && ct > this.timeRangeTimestamp.end) {
					_newStartTimestamp = this.timeRangeTimestamp.end - hfms
				}
				this.startTimestamp = _newStartTimestamp
				this.clearCanvas(this.width, this.height)
				this.draw()
			},

			/**
			 * @Author: 王林25
			 * @Date: 2021-01-20 15:29:52
			 * @Desc: 未按下显示鼠标所在时间
			 */
			hoverShow(x, noDraw) {
				const PX_PER_MS = this.width / this.totalMS // px/ms
				let time = this.startTimestamp + x / PX_PER_MS
				if (!noDraw) {
					this.clearCanvas(this.width, this.height)
					this.draw()
				}
				let h = this.height * (this.lineHeightRatio.hover === undefined ? 0.3 : this.lineHeightRatio.hover)
				this.drawLine(x, 0, x, h, 1, this.lineColor)
				this.ctx.setFillStyle(this.hoverTextColor)
				let t = this.hoverTimeFormat ? this.hoverTimeFormat(time) : dayjs(time).format('YYYY-MM-DD HH:mm:ss')
				let w = this.ctx.measureText(t).width
				this.ctx.fillText(t, x - w / 2, h + 20)
			},

			/**
			 * @Author: 王林25
			 * @Date: 2020-04-14 14:28:48
			 * @Desc: 鼠标移出事件
			 */
			onMouseout() {
				this.clearCanvas(this.width, this.height)
				this.draw()
			},

			/**
			 * @Author: 王林25
			 * @Date: 2021-01-20 16:22:04
			 * @Desc: 点击事件
			 */
			onClick(x, y) {
				const PX_PER_MS = this.width / this.totalMS // px/ms
				let time = this.startTimestamp + x / PX_PER_MS
				let date = dayjs(time).format('YYYY-MM-DD HH:mm:ss')
				let timeSegments = this.getClickTimeSegments(x, y)
				if (timeSegments && timeSegments.length > 0) {
					this.$emit('click_timeSegments', timeSegments, time, date, x)
				} else {
					this.onCanvasClick(time, date, x)
				}
			},

			/**
			 * @Author: 王林25
			 * @Date: 2021-01-20 16:24:54
			 * @Desc: 检测当前是否点击了某个时间段
			 */
			getClickTimeSegments(x, y) {
				let inItems = []
				this.drawTimeSegments((item) => {
					if (this.ctx.isPointInPath(x, y)) {
						inItems.push(item)
					}
				}, true)
				return inItems
			},

			/**
			 * @Author: 王林25
			 * @Date: 2021-01-20 11:14:30
			 * @Desc: 获取鼠标相当于时间轴的距离
			 */
			getClientOffset(e) {

				return new Promise((r, s) => {
					if (!this.$refs.timeLineContainer || !e) {
						this.clientOffset = [0, 0]
						// return [0, 0]
						r([0, 0])
					}
					let view = uni.createSelectorQuery().in(this).select(".timeLineContainer");

					view
						.boundingClientRect((data) => {
							// console.log("获取鼠标相当于时间轴的距离得到布局位置信息" + JSON.stringify(data));

							let {
								left,
								top
							} = data
							// console.log([e.clientX - left, e.clientY - top]);
							this.clientOffset = [e.clientX - left, e.clientY - top]
							// return [e.clientX - left, e.clientY - top]

							r([e.clientX - left, e.clientY - top])
						})
						.exec();
				})


			},

			/**
			 * @Author: 王林25
			 * @Date: 2020-04-14 14:25:43
			 * @Desc: 清除画布
			 */
			clearCanvas(w, h) {
				this.ctx.clearRect(0, 0, w, h)
				this.ctx.draw()
			},

			/**
			 * @Author: 王林25
			 * @Date: 2020-04-14 14:15:25
			 * @Desc: 时间格式化
			 */
			graduationTitle(datetime) {
				let time = dayjs(datetime)
				let res = ''
				if (this.formatTime) {
					res = this.formatTime(time)
				}
				if (res) {
					return res
				}
				if (this.yearMode) {
					return time.format('YYYY')
				} else if (this.yearMonthMode) {
					return time.format('YYYY-MM')
				} else if (
					time.hour() === 0 &&
					time.minute() === 0 &&
					time.millisecond() === 0
				) {
					return time.format('MM-DD')
				} else {
					return time.format('HH:mm')
				}
			},

			/**
			 * @Author: 王林25
			 * @Date: 2020-04-14 11:28:37
			 * @Desc: 绘制线段
			 */
			drawLine(x1, y1, x2, y2, lineWidth = 1, color = '#fff') {
				this.ctx.beginPath()
				this.ctx.setStrokeStyle(color)
				this.ctx.setLineWidth(lineWidth)
				this.ctx.moveTo(x1, y1)
				this.ctx.lineTo(x2, y2)
				this.ctx.stroke()

				this.ctx.draw(true)
			},

			/**
			 * @Author: 王林25
			 * @Date: 2021-01-20 15:57:11
			 * @Desc: 重新渲染
			 */
			reRender() {
				this.$nextTick(() => {
					this.clearCanvas(this.width, this.height)
					this.reset()
					this.setInitData()
					this.init()
					this.draw()
				})
			},

			/**
			 * @Author: 王林25
			 * @Date: 2021-01-20 16:07:53
			 * @Desc: 复位
			 */
			reset() {
				this.width = 0
				this.height = 0
				this.ctx = null
				this.currentZoomIndex = 0
				this.currentTime = 0
				this.startTimestamp = 0
				this.mousedown = false
				this.mousedownX = 0
				this.mousedownCacheStartTimestamp = 0
			},

			/**
			 * @Author: 王林25
			 * @Date: 2021-01-20 15:57:26
			 * @Desc: 设置当前时间
			 */
			 setTime(t) {
			      if (this.mousedown) {
			        return
			      }
			      let ts = typeof t === 'number' ? t : new Date(t).getTime()
			      this.currentTime = ts;
			      // this.startTimestamp = ts - this.totalMS / 2
			      this.fixStartTimestamp()
			      this.clearCanvas(this.width, this.height)
			      this.draw()
			      if (this.mousemoveX !== -1 && !this.isMobile) {
			        this.hoverShow(this.mousemoveX, true)
			      }
			    },
			/**
			 * @Author: 王林25
			 * @Date: 2021-01-20 19:32:39
			 * @Desc: 转发窗口时间轴的事件
			 */
			triggerClickWindowTimeSegments(data, index, item) {
				this.$emit('click_window_timeSegments', data, index, item)
			},

			/**
			 * @Author: 王林25
			 * @Date: 2021-01-21 09:58:17
			 * @Desc: 设置分辨率
			 */
			setZoom(index) {
				this.currentZoomIndex =
					index >= 0 && index < ZOOM.length ?
					index :
					5
				this.clearCanvas(this.width, this.height)
				this.startTimestamp = this.currentTime - this.totalMS / 2 // 当前时间-新的时间范围的一半
				this.draw()
			},

			/**
			 * @Author: 王林25
			 * @Date: 2021-01-21 10:15:30
			 * @Desc: 切换窗口时间轴的选中
			 */
			toggleActive(index) {
				this.windowListInner.forEach((item) => {
					item.active = false
				})
				this.windowListInner[index].active = true
				this.$emit('change_window_time_line', index, this.windowListInner[index])
			},

			/**
			 * @Author: 王林25
			 * @Date: 2021-01-21 10:47:28
			 * @Desc: 要观察的时间点，会返回该时间点的实时位置，你可以根据该位置来设置一些你的自定义元素，位置为相对于浏览器可视窗口的位置
			 */
			watchTime(time, callback, windowTimeLineIndex) {
				if (!time || !callback) {
					return
				}
				this.watchTimeList.push({
					time: typeof time === 'number' ? time : new Date(time).getTime(),
					callback,
					windowTimeLineIndex: typeof windowTimeLineIndex === 'number' ? windowTimeLineIndex - 1 : -1
				})
			},

			/**
			 * @Author: 王林25
			 * @Date: 2021-01-21 13:36:37
			 * @Desc: 窗口时间轴滚动
			 */
			onWindowListScroll() {
				this.updateWatchTime()
			},

			/**
			 * @Author: 王林25
			 * @Date: 2021-01-21 13:40:53
			 * @Desc: 尺寸重适应
			 */
			onResize() {
				this.init()
				this.draw()
				try {
					this.$refs.WindowListItem.forEach((item) => {
						item.init()
					})
				} catch (error) {}
			},

			// 时间轴点击事件
			onCanvasClick(...args) {
				this.$emit('click_timeline', ...args)
			}
		}
	}
</script>

<style lang="scss" scoped>
	.timeLineContainer {
		width: 100%;
		height: 100%;
		cursor: pointer;
		display: flex;
		flex-direction: column;

		.canvas {
			flex-grow: 0;
			flex-shrink: 0;
		}

		.windowList {
			width: 100%;
			height: 100%;
			overflow: auto;
			overflow-x: hidden;
			border-top: 1px solid rgba(153, 153, 153, 1);
			display: flex;
			flex-direction: column;

			&::-webkit-scrollbar {
				display: none;
			}
		}
	}
</style>